package com.dublbo.addressbook.util;

import com.dublbo.addressbook.entity.ContactPerson;

import java.io.*;
import java.util.*;

public class FileOperation {
    private final static String ENCODING = "UTF-8";
    private final static String TXT_FILE_SUFFIX = ".txt";
    private final static String CSV_FILE_SUFFIX = ".csv";

    /**
     * 从文本文件中一行一行读取数据
     *
     * @param filePath 读取的文件路径
     * @return 将文件的每一行内容存入 String，每一行放入 Vector 容器中
     * @throws IOException 路径是目录时抛出异常
     */
    private static Vector<String> readTxtFile(String filePath) throws IOException {
        File file = new File(filePath);
        Vector<String> res = new Vector<>();
        if (FileOperation.isTxtFile(file)) {
            InputStreamReader fr = new InputStreamReader(new FileInputStream(file), ENCODING); // 指定以UTF-8编码读入
            BufferedReader br = new BufferedReader(fr);
            String line;
            while ((line = br.readLine()) != null) {
                if (!line.equals("")) {
                    res.add(line);
                }
            }
            br.close();
            fr.close();
        }
        return res;
    }

    /**
     * 将 String 数据一行一行写入文本文件
     *
     * @param filePath 要写入文件的路径，不存在的文件能自动创建，文件原有的内容会覆盖掉
     * @param content  写入的内容
     * @throws IOException 当路径是目录时会抛异常
     */
    private static void writeTxtFile(String filePath, Vector<String> content) throws IOException {
        File file = new File(filePath);
        OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(file), ENCODING);
        BufferedWriter bw = new BufferedWriter(fw);

        for (String line : content) {   // 会覆盖原有的数据
            bw.write(line);
            bw.newLine();
        }

        bw.close();
        fw.close();
    }

    /**
     * 判断是否为文本文件
     *
     * @param file 待判断的文件对象
     * @return 如果是返回 true，否则返回 false
     */
    private static boolean isTxtFile(File file) {
        return file.isFile() && file.getName().endsWith(TXT_FILE_SUFFIX);
    }

    private static boolean isCsvFile(File file){
        return file.isFile() && file.getName().endsWith(CSV_FILE_SUFFIX);
    }

    /**
     * 在 联系人文件 的头部添加新增的内容
     *
     * @param insertPerson 要插入的联系人对象
     * @throws IOException 文件读取错误
     */
    public static void insertCPersonTxtFileRow(ContactPerson insertPerson) throws IOException {
        // 先读取文件，再将改变后的内容写入文件
        Vector<String> strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);
        // 在第一行插入数据
        strVec.add(0, insertPerson.decode());
        FileOperation.writeTxtFile(Constant.ALL_PERSON_INFO_FILEPATH, strVec);
    }

    /**
     * 删除 联系人文件 的某行内容
     *
     * @param key 要删除的联系人的主键（名称）
     * @throws IOException 文件读取错误
     */
    public static void deleteCPersonTxtFileRow(String key) throws IOException {
        // 删除容器中的记录
        Vector<String> strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);
        int totalRow = strVec.size();

        assert totalRow > 0;

        for (int i = 0; i < totalRow; i++) {
            if (key.equals(strVec.get(i).split(Constant.FILE_SPLITTER)[Constant.PERSON_INFO.NAME.ordinal()])) {
                strVec.removeElementAt(i);
                break;
            }
        }
        // 将删除后的数据写入文件
        if (strVec.size() == 0) {
            File file = new File(Constant.ALL_PERSON_INFO_FILEPATH);
            file.delete();
            file.createNewFile();
        } else {
            FileOperation.writeTxtFile(Constant.ALL_PERSON_INFO_FILEPATH, strVec);
        }
    }

    /**
     * 修改 联系人文件 中某一行内容
     *
     * @param modifyPerson 要修改的联系人对象
     * @throws IOException 文件读取错误
     */
    public static void modifyCPersonTxtFileRow(ContactPerson modifyPerson) throws IOException {
        Vector<String> strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);
        int totalRow = strVec.size();

        assert (totalRow > 0);

        for (int i = 0; i < totalRow; i++) {
            if (modifyPerson.getName().equals(strVec.get(i).split(Constant.FILE_SPLITTER)[Constant.PERSON_INFO.NAME.ordinal()])) {
                strVec.setElementAt(modifyPerson.decode(), i);
                break;
            }
        }
        // 将改变写入文件
        FileOperation.writeTxtFile(Constant.ALL_PERSON_INFO_FILEPATH, strVec);
    }

    /**
     * 查询文本文件中的某些行（过滤条件：按名称模糊查询）
     *
     * @param name 使用名称作为模糊查询
     * @return 联系人列表 List<ContactPerson>
     * @throws IOException 读取文件异常
     */
    public static List<ContactPerson> queryCPersonTxtFile(String name, String groupFilter) throws IOException {
        Vector<String> strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);
        List<ContactPerson> personList = new ArrayList<>();
        if (groupFilter.equals("")) {  // 所有联系人
            for (String s : strVec) {
                String tmp = s.split(Constant.FILE_SPLITTER)[Constant.PERSON_INFO.NAME.ordinal()];
                if (tmp.contains(name)) {
                    ContactPerson person = new ContactPerson(s);
                    personList.add(person);
                }
            }
        } else {
            for (String s : strVec) {
                String tmp1 = s.split(Constant.FILE_SPLITTER)[Constant.PERSON_INFO.NAME.ordinal()];
                String[] tmpArr = s.split(Constant.FILE_SPLITTER)[Constant.PERSON_INFO.GROUP.ordinal()].split(Constant.BELONG_GROUP_SPLITTER);

                if (findGroup(tmpArr, groupFilter) > -1 && tmp1.contains(name)) {
                    ContactPerson person = new ContactPerson(s);
                    personList.add(person);
                }
            }
        }
        return personList;
    }

    /**
     * 查找某个联系人的所在组中某个组位置
     *
     * @param keyArr 联系人所在的分组
     * @param group  要查找的组
     * @return -1：不存在， > -1：该分组所在位置下标
     */
    private static int findGroup(String[] keyArr, String group) {
        for (int i = 0; i < keyArr.length; i++) {
            if (keyArr[i].equals(group)) {
                return i;
            }
        }
        return -1;
    }

    // 查询联系组文件里的所有内容
    public static List<String> queryGroupTxtFile() {
        List<String> res = new ArrayList<>();
        try {
            Vector<String> strVec = FileOperation.readTxtFile(Constant.GROUP_INFO_FILEPATH);
            res.addAll(strVec);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return res;
    }

    // 通过主键查询某个联系人
    public static ContactPerson getCPersonByKey(String key) {
        Vector<String> strVec;
        try {
            strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);

            assert strVec.size() > 0;

            for (String s : strVec) {
                if (key.equals(s.split(Constant.FILE_SPLITTER)[Constant.PERSON_INFO.NAME.ordinal()])) {
                    return new ContactPerson(s);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 修改联系人的头像
     *
     * @param person        要修改的联系人对象
     * @param sourceImgPath 原头像地址
     * @param newImagePath  上传后的头像地址
     * @throws IOException 文件读取异常
     */
    public static void modifyCPersonAvatar(ContactPerson person, String sourceImgPath, String newImagePath) throws IOException {
        // 1.将文件存入系统指定文件夹
        File file = new File(sourceImgPath);
        FileInputStream fis = new FileInputStream(file);
        byte[] bit = new byte[fis.available()];
        fis.read(bit);
        File newFile = new File(newImagePath);
        FileOutputStream fos = new FileOutputStream(newFile);
        fos.write(bit);
        fis.close();
        fos.close();
        if (person.getPhoto() != null && person.getPhoto().equals(""))  // 删除原有头像
            new File(person.getPhoto()).delete();
        // 2.修改联系人文件
        person.setPhoto(newImagePath);
        modifyCPersonTxtFileRow(person);
    }

    // 判断联系人是否已存在
    public static boolean cPersonIsExist(String personName) {
        try {
            Vector<String> strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);
            for (String s : strVec) {
                if (personName.equals(s.split(Constant.FILE_SPLITTER)[Constant.PERSON_INFO.NAME.ordinal()])) {
                    return true;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 将某个联系人复制到某个联系组
     *
     * @param personName 联系人对象主键
     * @param destGroup  复制的目的分组
     * @throws IOException 文件读取异常
     */
    public static void copyToGroup(String personName, String destGroup) throws IOException {
        Vector<String> strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);
        int totalRow = strVec.size();

        for (int i = 0; i < totalRow; i++) {
            ContactPerson person = new ContactPerson(strVec.get(i));
            if (personName.equals(person.getName())) {
                String g = person.getGroup();
                if (g.equals("")) { // 直接加入分组
                    person.setGroup(destGroup);
                } else { // 已经有分组了
                    String[] tmpArr = person.getGroup().split(Constant.BELONG_GROUP_SPLITTER);
                    // 判断是否已经加入过该分组，没有加入就添加该分组，否则直接退出
                    if (FileOperation.findGroup(tmpArr, destGroup) == -1) {
                        person.setGroup(String.join(Constant.BELONG_GROUP_SPLITTER, g, destGroup));
                    }
                }
                strVec.setElementAt(person.decode(), i);
                break;
            }
        }
        // 将改变写回文件
        FileOperation.writeTxtFile(Constant.ALL_PERSON_INFO_FILEPATH, strVec);
    }

    // 判断联系组是否存在
    public static boolean groupIsExist(String groupName) {
        try {
            Vector<String> strVec = FileOperation.readTxtFile(Constant.GROUP_INFO_FILEPATH);
            for (String s : strVec) {
                if (groupName.equals(s)) {
                    return true;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    // 插入联系组文件 一行数据
    public static void insertGroupTxtFileRow(String groupName) throws IOException {
        // 先读取文件，再将改变后的内容写入文件
        Vector<String> strVec = FileOperation.readTxtFile(Constant.GROUP_INFO_FILEPATH);
        // 在最后一行插入数据
        strVec.add(groupName);
        FileOperation.writeTxtFile(Constant.GROUP_INFO_FILEPATH, strVec);
    }

    /**
     * 删除 联系组文件 中的一条记录
     *
     * @param groupName 要删除的联系组名
     * @throws IOException 文件读取错误
     */
    public static void deleteGroupTxtFileRow(String groupName) throws IOException {
        // 1. 移出该组的所有联系人
        FileOperation.cleanCPersonFromGroup(groupName);
        // 2. 从联系组文件中删除该组
        Vector<String> groups = FileOperation.readTxtFile(Constant.GROUP_INFO_FILEPATH);
        int len = groups.size();
        assert len > 0;
        for (int i = 0; i < len; i++) {
            if (groups.get(i).equals(groupName)) {
                groups.removeElementAt(i);
                break;
            }
        }
        // 将删除后的数据写入文件
        if (groups.size() == 0) {
            File file = new File(Constant.GROUP_INFO_FILEPATH);
            file.delete();
            file.createNewFile();
        } else {
            FileOperation.writeTxtFile(Constant.GROUP_INFO_FILEPATH, groups);
        }

    }

    /**
     * 将某联系组的所有联系人移出，但不删除联系人
     *
     * @param groupName 要操作的联系组名称
     * @throws IOException 文件读取错误
     */
    private static void cleanCPersonFromGroup(String groupName) throws IOException {
        Vector<String> strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);

        int len = strVec.size();

        assert len > 0;

        for (int i = 0; i < len; i++) {
            ContactPerson person = new ContactPerson(strVec.get(i));
            String[] tmpArr = person.getGroup().split(Constant.BELONG_GROUP_SPLITTER);
            int index = FileOperation.findGroup(tmpArr, groupName);
            if (index > -1) {
                tmpArr[index] = "";
                person.setGroup(FileOperation.transGroupStr(tmpArr));
                System.out.println(person.getName() + " " + person.getGroup());
                strVec.setElementAt(person.decode(), i);
            }
        }
        // 将改变重新写入文件
        FileOperation.writeTxtFile(Constant.ALL_PERSON_INFO_FILEPATH, strVec);
    }

    private static String transGroupStr(String[] tmpArr) {
        int len = tmpArr.length;
        assert len > 0;
        if (len == 1) { // 删除后就是未分组联系人了
            return "";
        }
        // 删除后还有分组
        StringBuilder sb = new StringBuilder();
        int sign = 1;
        for (String s : tmpArr) {
            if (!s.equals("")) {
                if (sign == 1) { // 第一个字符串不加分隔符
                    sb.append(s);
                    sign = -1;
                } else {
                    sb.append(Constant.BELONG_GROUP_SPLITTER).append(s);
                }
            }
        }
        return sb.toString();
    }

    /**
     * 修改 联系组文件中的一行内容
     *
     * @param rawGroupName    要修改的联系组名称
     * @param modifyGroupName 修改之后的联系组名称
     * @throws IOException 文件读取错误
     */
    public static void modifyGroupTxtFileRow(String rawGroupName, String modifyGroupName) throws IOException {
        Vector<String> groups = FileOperation.readTxtFile(Constant.GROUP_INFO_FILEPATH);
        int len = groups.size();
        for (int i = 0; i < len; i++) {
            if (rawGroupName.equals(groups.get(i))) {
                groups.setElementAt(modifyGroupName, i);
                break;
            }
        }
        FileOperation.writeTxtFile(Constant.GROUP_INFO_FILEPATH, groups);
    }

    /**
     * 将某个联系人移动到某个分组
     *
     * @param personName 联系人对象主键
     * @param rawGroup   原始分组
     * @param destGroup  移动后的分组
     * @throws IOException 文件读取错误
     */
    public static void moveToGroup(String personName, String rawGroup, String destGroup) throws IOException {
        Vector<String> strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);
        int totalRow = strVec.size();

        for (int i = 0; i < totalRow; i++) {
            ContactPerson person = new ContactPerson(strVec.get(i));
            if (personName.equals(person.getName())) {
                String[] tmpArr = person.getGroup().split(Constant.BELONG_GROUP_SPLITTER);
                int index = FileOperation.findGroup(tmpArr, rawGroup);
                if (index > -1) { // 包含原始组 rawGroup
                    if (FileOperation.findGroup(tmpArr, destGroup) > -1) { // 已经在 目的分组，则删除 rawGroup分组 就行
                        tmpArr[index] = "";
                        person.setGroup(FileOperation.transGroupStr(tmpArr));
                    } else {
                        tmpArr[index] = destGroup; // 替换成新的分组 destGroup
                        person.setGroup(String.join(Constant.BELONG_GROUP_SPLITTER, tmpArr));
                    }
                }
                strVec.setElementAt(person.decode(), i);
                break;
            }
        }
        // 将改变写回文件
        FileOperation.writeTxtFile(Constant.ALL_PERSON_INFO_FILEPATH, strVec);
    }


    /**
     * 导出所有联系人
     * @param exportPath 输出文件路径
     * @throws Exception 未能正确保存文件就会抛出异常
     */
    public static void exportCPerson(String exportPath) throws Exception {
        File file = new File(exportPath);
        if(FileOperation.isCsvFile(file)){  // 判断输出文件是否为 csv 文件
            Vector<String> strVec = FileOperation.readTxtFile(Constant.ALL_PERSON_INFO_FILEPATH);
            int len = strVec.size();
            assert len > 0;

            BufferedWriter csvWriter = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(file), "GB2312"), 1024);
            // 写入头部
            Vector<Object> headRow = new Vector<>();
            csvWriter.write("姓名,电话号码,邮箱,所在组,生日,工作单位,家庭地址,邮编,备注");
            csvWriter.newLine();

            for(String s : strVec){
                csvWriter.write(new ContactPerson(s).toString());
                csvWriter.newLine();
            }

            csvWriter.close();
        }
    }

//    private static void writeCsvFileRow(BufferedWriter csvWriter, Vector<Object> rowData) throws IOException {
//        for(Object data : rowData){
//            StringBuilder sb = new StringBuilder();
//            String rowStr = sb.append(data).append(",").toString();
//            csvWriter.write(rowStr);
//        }
//        csvWriter.newLine();
//    }
}